;
;  PROGRAM:  PATH
;  VERSION:  1.0
;  AUTHOR:  RICHARD CONN
;  DATE:  12 JAN 83
;  PREVIOUS VERSIONS:  NONE
;
VERS	EQU	10

;
;	PATH allows the user to do two things -- display the current path
; and set a new path.  Named directories may be used in the definition of
; the new path.
;
;	PATH is invoked by the following forms:
;		PATH			<-- Display Path
;		PATH path-expression	<-- Set Path
;		PATH //			<-- Print Help
;

;
;  CP/M Constants
;
cpm	equ	0	;base
bdose	equ	cpm+5
fcb	equ	cpm+5ch
tbuff	equ	cpm+80h
cr	equ	0dh
lf	equ	0ah

;
;  SYSLIB Routines
;
	ext	cline,zgpins,zdname,print,codend,eval10
	ext	cout,pstr,padc,retud

;
;	This program is Copyright (c) 1982, 1983 by Richard Conn
;	All Rights Reserved
;
;	ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;	The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;  Branch to Start of Program
;
	jmp	start

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	0FFH	; EXTERNAL PATH?
MCREQD:
	DB	000H	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	0FFH	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	0FFH	; NAMED DIRECTORIES?
Z2CLASS:
	DB	0	; CLASS 0
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  Start of Program
;
start:
	call	zgpins	; init ZCPR2 buffers
	lxi	h,tbuff	; pt to buffer
	call	cline	; save it as string
	shld	cmdline	; save ptr to command line
	call	retud	; get current disk and user
	mov	a,b
	sta	cdisk	; set disk
	mov	a,c
	sta	cuser	; set user
;
;  Print Banner
;
	call	print
	db	'PATH  Version '
	db	vers/10+'0','.',(vers mod 10)+'0',0

;
;  Check for Help
;
	lda	fcb+1	; get first char
	cpi	'/'	; help?
	jnz	start1
	call	print
	db	cr,lf,'	PATH allows the user to display his current path'
	db	cr,lf,'and set a new path.  It is invoked by one of the forms:'
	db	cr,lf
	db	cr,lf,'		PATH			<-- Display Path'
	db	cr,lf,'		PATH path-expression	<-- Set Path'
	db	cr,lf,'		PATH //			<-- Print Help'
	db	cr,lf,0
	ret

;
;  Check for Error and Continue if not:
;	Load NAMES.DIR and check for function
;
start1:
	lda	epavail	; external path available?
	ora	a	; 0=no
	jnz	start2
	call	print
	db	cr,lf,'Error -- External Path Not Defined -- Aborting',0
	ret
start2:
	call	codend	; pt to buffer
	call	zdname	; load NAMES.DIR
	jnz	start3
	mvi	c,0	; if no NAMES.DIR or overflow, set no entries
start3:
	shld	ddir	; save ptr to disk buffer
	mov	a,c	; set entry count
	sta	dentry
	lxi	h,tempath	; pt to temporary path
	shld	pathptr	; set path ptr
	lhld	cmdline	; check command line for text
	call	sblank	; skip to non-blank
	shld	cmdline	; set ptr to next element
	ora	a	; EOL=display function
	jz	pdisp

;
;  **** Set New Path ****
;	HL pts to next element
;
pbuild:
	lhld	cmdline	; pt to next element
	call	sblank	; skip to non-blank
	mov	a,m	; get first char of next element
	ora	a	; EOL?
	jz	pbdone	; done if so, store path and display
	shld	token	; save ptr to first byte
	lda	cindic	; get current disk indicator
	cmp	m	; is it current?
	jz	pbdu	; DU: form
	mov	a,m	; get first char again
	sui	'A'	; convert to number
	jc	pbdir	; DIR: form
	mov	b,a	; save number
	lda	mdisk	; compare to max disk number
	mov	c,a
	mov	a,b
	cmp	c	; in range?
	jnc	pbdir	; DIR: form if not
	inx	h	; pt to next char -- may be DU or DIR
	lda	cindic	; get current indicator
	mov	b,a	; ... in B
	mov	a,m	; get next part of element
	cmp	b	; current?
	jz	pbdu	; is a DU: form
digtst:
	cpi	':'	; colon ends it
	jz	pbdu	; is a DU: form
	cpi	' '	; space ends it
	jz	pbdu
	ora	a	; EOL ends it
	jz	pbdu
	cpi	'0'	; must be a digit
	jc	pbdir	; DIR: form if not in range
	cpi	'9'+1
	jnc	pbdir
	inx	h	; pt to next
	mov	a,m	; get it
	jmp	digtst
;
;  It is a DU: form
;
pbdu:
	lhld	pathptr	; pt to path entry
	xchg		; ... in DE
	lhld	token	; pt to token
	lda	cindic	; get current indicator
	mov	b,a
	mov	a,m	; current?
	cmp	b
	jz	pbdu1
	sui	'A'-1	; convert to number from 1 to n
pbdu1:
	stax	d	; save disk element
	inx	h	; pt to next
	inx	d
	mov	a,m	; current user?
	inx	h	; pt to after user in case of match to cindic
	cmp	b	; match cindic?
	jz	pbdu2
	dcx	h	; pt to first digit
	push	d	; save ptr to path
	call	eval10	; convert to number
	mov	a,d	; range error?
	ora	a
	jnz	rangerr
	lda	muser	; check for max user
	inr	a
	mov	b,a
	mov	a,e
	cmp	b
	jnc	rangerr
	pop	d	; get ptr to path
pbdu2:
	stax	d	; store user number
	inx	d
	mov	a,m	; ending with colon?
	cpi	':'
	jnz	pbdu3
	inx	h	; skip over colon
pbdu3:
	shld	cmdline	; save ptr to next command line entry
	xchg
	shld	pathptr	; save ptr to next path entry
	jmp	pbuild	; continue processing
;
;  Build DIR: form
;
pbdir:
	lhld	ndradr	; memory-based directory available?
	mov	a,h
	ora	l
	jz	ddscan	; scan disk dir if not
	inx	h	; pt to entry count
	mov	c,m	; get entry count in C
	inx	h	; pt to first entry
	call	dirscan	; scan directory
	jnz	gotud	; store disk/user
ddscan:
	lhld	ddir	; pt to disk dir
	lda	dentry	; get entry count
	mov	c,a	; ... in C
	call	dirscan	; scan directory
	jnz	gotud
;
;  Entry not found
;
rangerr:
	call	print
	db	cr,lf
	db	cr,lf,'Invalid Path Expression Element -- Error Flagged at:'
	db	cr,lf,'	-->',0
	lhld	token	; print string starting at token
	call	pstr
	call	print
	db	cr,lf,'This may be an invalid DU: form (disk or user out of '
	db	'range)'
	db	cr,lf,'or an undefined named directory.'
	db	cr,lf
	db	cr,lf,'Aborting to CP/M',0
	jmp	cpm
;
;  Got User and Disk -- Store in Path
;
gotud:
	lhld	pathptr	; get ptr to path
	mov	m,b	; store disk
	inx	h
	mov	m,c	; store user
	inx	h	; pt to next
	shld	pathptr
	lhld	token	; skip over token
gotud1:
	mov	a,m	; skip to space or EOL
	inx	h	; pt to next
	ora	a	; EOL?
	jz	gotud2
	cpi	' '	; space?
	jnz	gotud1
gotud2:
	dcx	h	; pt to EOL or space
	shld	cmdline	; set ptr to next element
	jmp	pbuild	; continue building
;
;  Path Building is Done -- TEMPATH contains new path
;
pbdone:
	lhld	pathptr	; store ending zero in path
	mvi	m,0
	lhld	epadr	; pt to external path
	xchg		; ... in DE
	lxi	h,tempath	; copy tempath into external path
pcopy:
	mov	a,m	; get disk
	stax	d	; put disk
	ora	a	; end of path?
	jz	pdisp	; done if so and display
	inx	h	; pt to user
	inx	d
	mov	a,m	; get user
	stax	d	; put user
	inx	h	; pt to next disk
	inx	d
	jmp	pcopy

;
;  **** Display Path Function ****
;
pdisp:
	call	print
	db	cr,lf,'Current Path in Symbolic Form -- ',cr,lf,'	',0
	lhld	epadr	; pt to external path
	lda	cindic	; get current indicator
	mov	d,a	; ... in D
pdisp1:
	mov	a,m	; get disk
	ora	a	; done?
	jz	adisp
	cmp	d	; current?
	jz	pdisp2
	adi	'@'	; convert to letter
pdisp2:
	call	cout	; print disk letter
	inx	h	; pt to user
	mov	a,m	; get user number
	cmp	d	; current?
	jnz	pdisp3
	call	cout	; print cindic
	jmp	pdisp4
pdisp3:
	call	padc	; print user number
pdisp4:
	mvi	a,':'	; print colon
	call	cout
	inx	h	; pt to next element
	mov	a,m	; done?
	ora	a	; 0=yes
	jz	adisp
	call	print
	db	' --> ',0
	jmp	pdisp1
;
;  Print Absolute Path
;
adisp:
	call	print
	db	cr,lf,'Current Path in Absolute Form --',cr,lf,'	',0
	call	curud	; get current user/disk
	lda	cindic	; get current indicator
	mov	d,a	; ... in D
	lhld	epadr	; pt to path
adisp1:
	mov	a,m	; get disk
	ora	a	; done?
	jz	ndisp
	cmp	d	; current?
	jnz	adisp2
	mov	a,b	; get current disk
	inr	a	; adjust to 1 to n
adisp2:
	adi	'@'	; convert to letter
	call	cout	; print disk letter
	inx	h	; pt to user
	mov	a,m	; get user
	cmp	d	; current?
	jnz	adisp3
	mov	a,c	; get current user
adisp3:
	call	padc	; print user
	mvi	a,':'
	call	cout
	inx	h	; pt to next
	mov	a,m	; done?
	ora	a
	jz	ndisp
	call	print
	db	' --> ',0
	jmp	adisp1
;
;  Print Named Path
;
ndisp:
	call	print
	db	cr,lf,'Current Path in Named Directory Form --',cr,lf,'	',0
	lhld	epadr	; pt to external path
ndisp1:
	lda	cindic	; get current indicator
	mov	d,a	; ... in D
	call	curud	; get current user and disk in C and B
	mov	a,m	; get disk
	ora	a	; done?
	rz
	cmp	d	; current?
	jz	ndisp2
	mov	b,a	; disk in B
	dcr	b	; adjust to 0 to n-1
ndisp2:
	inx	h	; pt to user
	mov	a,m	; get it
	cmp	d	; current?
	jz	ndisp3
	mov	c,a	; user in C
ndisp3:
	inx	h	; pt to next
	push	h	; save ptr
	call	udscan	; scan dirs for user/disk and print its name
	pop	h	; get ptr
	mvi	a,':'
	call	cout
	mov	a,m	; done?
	ora	a
	rz
	call	print
	db	' --> ',0
	jmp	ndisp1


;
;  Utilities
;

;
;  Scan Memory-Based Directory for Name Pted to by TOKEN
;	On entry, HL pts to first entry and C=number of entries
;	On exit, return with NZ and BC=disk/user if found
;
dirscan:
	mov	a,c	; any entries?
	ora	a
	rz		; error return
dirsl:
	push	h	; save ptr to entry
	push	b	; save char count
	call	tokscan	; check for token match
	pop	b	; get char count
	pop	h	; get entry ptr
	jz	dirsfnd	; found?
	lxi	d,10	; skip to next entry
	dad	d
	dcr	c	; count down
	jnz	dirsl
	ret		; return with Z if not found
dirsfnd:
	mov	b,m	; entry found, so get disk number
	inr	b	; adjust to 1 to n
	inx	h	; pt to user
	mov	c,m	; get user
	mvi	a,0ffh	; OK return
	ora	a
	ret
;
;  Scan entry pted to by HL to see if it contains the TOKEN
;
tokscan:
	xchg		; save ptr in DE
	lhld	token	; pt to token
	xchg		; HL pts to entry, DE pts to token
	inx	h	; pt to entry in dir
	inx	h
	mvi	b,8	; up to 8 bytes
toks1:
	ldax	d	; get token char
	cpi	':'	; end of token?
	jz	toks2
	cpi	' '	; space?
	jz	toks2
	ora	a	; EOL?
	jz	toks2
	cmp	m	; match?
	rnz		; error abort if no match
	inx	h	; pt to next
	inx	d
	dcr	b	; count down
	jnz	toks1
	ldax	d	; full 8 chars, so token char must be a delim
	cpi	':'	; ok?
	rz
	cpi	' '	; ok if space
	rz
	ora	a	; ok if EOL
	ret
toks2:
	mov	a,m	; this must be a space for match
	cpi	' '
	ret
;
;  Skip to non-blank
;
sblank:
	mov	a,m	; get char
	inx	h	; pt to next
	cpi	' '	; space?
	jz	sblank
	dcx	h	; pt to non-blank
	ret
;
;  Scan directories for user and disk in C and B
;	Print name if found or Noname if not
;
udscan:
	lhld	ndradr	; check memory-based first
	mov	a,h
	ora	l
	jz	udscan1	; none
	inx	h	; get entry count
	mov	d,m	; ... in D
	inx	h	; pt to first element
	call	udndscn	; scan named dir
	jz	udscan2	; match
udscan1:
	lhld	ddir	; pt to disk-based dir
	lda	dentry	; number of entries
	mov	d,a	; entry count in D
	call	udndscn	; scan named dir
	jnz	udscan3	; return no name
udscan2:
	mvi	b,8	; 8 chars max
udsprn:
	mov	a,m	; get name char
	cpi	' '	; done?
	rz
	call	cout	; print char
	inx	h	; pt to next
	dcr	b	; count down
	jnz	udsprn
	ret
udscan3:
	call	print
	db	'Noname',0
	ret
;
;  Scan dir pted to by HL for D elements
;	BC=Disk/User
;	Return with Z set and HL pting to name if found
;
udndscn:
	mov	a,m	; get disk
	inx	h	; pt to user
	cmp	b	; match so far?
	jnz	udnd1
	mov	a,m	; get user
	cmp	c	; match?
	jnz	udnd1
	inx	h	; pt to name
	xra	a	; return with zero for found
	ret
udnd1:
	push	d	; save count
	lxi	d,9	; pt to next element
	dad	d
	pop	d	; get count
	dcr	d	; count down
	jnz	udndscn
	mvi	a,0ffh	; not found
	ora	a
	ret

;
;  Return Current Disk and User (in B and C)
;
curud:
	lda	cdisk	; get disk
	mov	b,a	; ... in B
	lda	cuser	; get user
	mov	c,a	; ... in C
	ret

;
;  Buffers
;
cdisk:
	ds	1	; current disk
cuser:
	ds	1	; current user
cmdline:
	ds	2	; ptr to next char in command line
token:
	ds	2	; ptr to current token
pathptr:
	ds	2	; ptr to next path entry
ddir:
	ds	2	; ptr to disk dir in memory
dentry:
	ds	1	; number of entries in disk dir
tempath:
	ds	50	; allow for 25 element path

	end
